package com.wlyuan.index.domain.index;

import com.wlyuan.index.domain.index.valueobj.CombineRelation;
import com.wlyuan.index.domain.index.valueobj.Quotation;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.DateFormat;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.BiConsumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;


/**
 * @author yuanjie
 */
@Data
@Slf4j
@Document(indexName = "full_combine_index")
public class CombineIndex {
    @Data
    public static class Waybill {
        @Field(type = FieldType.Long)
        private Long waybillId;
        @Field(type = FieldType.Keyword)
        private String contractNo;
        @Field(type = FieldType.Long)
        private Long customerId;
        @Field(type = FieldType.Keyword)
        private String customerName;
        @Field(type = FieldType.Long)
        private Long consigneeId;
        @Field(type = FieldType.Keyword)
        private String consigneeName;
    }

    @Id
    private Long combineId;

    @ApiModelProperty("合同编号")
    @Field(type = FieldType.Keyword)
    private String contractNo;

    @ApiModelProperty("合单号")
    private String combineNo;

    @ApiModelProperty("报价单ID")
    @Field(type = FieldType.Long)
    private Long quotationId;

    @ApiModelProperty("发货方ID")
    @Field(type = FieldType.Long)
    private Long customerId;

    @ApiModelProperty("发货方名称")
    @Field(type = FieldType.Keyword)
    private String customerName;

    @Field(type = FieldType.Integer)
    private Integer customerType;

    @ApiModelProperty("承运方ID")
    @Field(type = FieldType.Long)
    private Long carrierId;

    @ApiModelProperty("承运方名称")
    @Field(type = FieldType.Keyword)
    private String carrierName;

    @Field(type = FieldType.Integer)
    private Integer carrierType;

    @ApiModelProperty("司机ID")
    @Field(type = FieldType.Long)
    private Long driverId;

    @ApiModelProperty("司机名称")
    @Field(type = FieldType.Keyword)
    private String driverName;

    @Field(type = FieldType.Long)
    private Long consigneeId;

    @Field(type = FieldType.Keyword)
    private String consigneeName;

    @ApiModelProperty("总数量")
    @Field(type = FieldType.Integer)
    private Integer totalQuantity;

    @ApiModelProperty("总重量")
    @Field(type = FieldType.Double)
    private BigDecimal calculateWeight;

    @ApiModelProperty("总体积")
    @Field(type = FieldType.Double)
    private BigDecimal calculateVolume;

    @ApiModelProperty("总体积")
    @Field(type = FieldType.Double)
    private BigDecimal calculateValue;

    @ApiModelProperty("总费用")
    @Field(type = FieldType.Double)
    private BigDecimal totalFee;

    @ApiModelProperty("发货时间")
    @Field(type = FieldType.Date, format = DateFormat.custom, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
    private LocalDateTime fromTime;

    @ApiModelProperty("到货时间")
    @Field(type = FieldType.Date, format = DateFormat.custom, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
    private LocalDateTime toTime;

    @Field(type = FieldType.Keyword)
    private String trackNo;

    @Field(type = FieldType.Integer)
    private Integer trackType;

    @ApiModelProperty("创建时间")
    @Field(type = FieldType.Date, format = DateFormat.custom, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
    private LocalDateTime createAt;

    @ApiModelProperty("运单信息")
    @Field(type = FieldType.Nested)
    private List<Waybill> waybills;

    @Field(type = FieldType.Nested)
    private List<CombineRelation> relations;

    @Field(type = FieldType.Integer)
    private Integer closeStatus;

    @Field(type = FieldType.Integer)
    private Integer closeType;

    @Field(type = FieldType.Keyword)
    private List<String> keywords;

    @Field(type = FieldType.Boolean)
    private Boolean endBill;

    @Field(type = FieldType.Boolean)
    private Boolean hasQuote;

    @Field(type = FieldType.Boolean)
    private Boolean hasFees;

    @Field(type = FieldType.Long)
    private Long tenantId;

    @Field(type = FieldType.Long)
    private Long version;

    @Field(type = FieldType.Date, format = DateFormat.custom, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
    private LocalDateTime indexAt;

    private void addRelation(CombineRelation relation) {
        synchronized (this) {
            if (null == this.relations) {
                this.relations = new ArrayList<>();
            }
        }
        this.relations.add(relation);
    }

    public CombineIndex addRelation(CombineRelation relation, BiConsumer<CombineRelation, CombineRelation> assembler) {
        if (CollectionUtils.isEmpty(this.relations)) {
            this.addRelation(relation);
            return this;
        }

        AtomicBoolean hasApplied = new AtomicBoolean();
        this.relations(relation.getTenantId()).forEach(origin -> {
            assembler.accept(origin, relation);
            hasApplied.set(true);
        });

        if (!hasApplied.get()) {
            this.addRelation(relation);
        }
        return this;
    }

    public CombineIndex checkRelations(List<Long> tenantIds) {
        // 不在tenantId列表的relation需要移除
        this.relations = this.relations.stream().filter(relation -> tenantIds.contains(relation.getTenantId()))
                .collect(Collectors.toList());
        return this;
    }


    public CombineIndex addQuotation(Quotation quotation, Long tenantId) {
        this.relations(tenantId).forEach(relation -> relation.addQuotation(quotation));
        return this;
    }

    private Stream<CombineRelation> relations() {
        if (CollectionUtils.isEmpty(this.relations)) {
            return Stream.empty();
        }
        return this.relations.stream().filter(Objects::nonNull);
    }

    private Stream<CombineRelation> relations(Long tenantId) {
        return this.relations().filter(relation -> relation.getTenantId().equals(tenantId));
    }

    public void buildRelations() {
        this.relations().forEach(relation -> relation.buildQuotation(this));
    }

    public boolean endBill() {
        return BooleanUtils.isTrue(this.endBill);
    }

    public boolean canQuote() {
        return !this.endBill();
    }
}